﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using DAL.Repositorios;


namespace DAL.AbstractRepositories
{
   /// <summary>
   /// el primer elemento deberia al que tenga mas asociaciones por uso. Para mayor eficiencia
   /// </summary>
    public abstract class AbstractRelationRepository : IRepositoryRelations<long, long>
    {
        
        private Dictionary<long, HashSet<long>> inserted;
        private Dictionary<long, HashSet<long>> deleted;
        /// <summary>
        /// Al llamar a save se ejecuta y se pierde la referencia
        /// </summary>

        private Action actionBeforeSave;
        public Action ActionBeforeSave {
            set { 
                if (value == null) 
                    throw new ArgumentNullException();

                actionBeforeSave = value;
            
            }

            get { return actionBeforeSave; }
        }
        
        /// <summary>
        /// Al llamar a save se ejecuta y se pierde la referencia
        /// </summary>
        /// 
        private Action actionAfterSave;
        public Action ActionAfterSave 
        {
            set 
            {
                if (value == null)
                    throw new ArgumentNullException();
                
                actionAfterSave = value;
            }

            get { return actionAfterSave; }
        }

        public AbstractRelationRepository()
        {
            ActionBeforeSave = () => { };
            ActionAfterSave = () => { };
            inserted = new Dictionary<long, HashSet<long>>();
            deleted = new Dictionary<long, HashSet<long>>();
        }


        public void Asign(long key, long related)
        {
            if (!inserted.ContainsKey(key))
                inserted.Add (key, new HashSet<long>());

            inserted[key].Add(related);
        }

        public void Remove(long key, long related)
        {
            if (!deleted.ContainsKey(key))
                deleted.Add(key, new HashSet<long>());

            deleted[key].Add(related);
        }

   

        public void Save()
        {
            using (TransactionScope scope = new TransactionScope())
            {

                try
                {
                        ActionBeforeSave();
                    
                    

                    foreach (KeyValuePair<long, HashSet<long>> t in deleted)
                    {
                        foreach (long id in t.Value)
                            ConsultaRemover(t.Key, id);
                    }

                    foreach (KeyValuePair<long, HashSet<long>> t in inserted) 
                    { 
                        foreach(long id in t.Value)
                            ConsultaAsignar(t.Key, id);
                    }

                        ActionAfterSave();   
                    
                    scope.Complete();
                }
                finally
                {
                    Clear();
                    ActionAfterSave = () => { };
                    ActionBeforeSave = () => { };
                }

            }
        }
        

        public void Clear()
        {
            inserted.Clear();
            deleted.Clear();
        }
        public abstract void RemoveAllFromKey(long id);

        public abstract void RemoveAllFromRelated(long key);
        protected abstract  void ConsultaAsignar(long id, long id2);

        protected abstract void ConsultaRemover(long id, long id2);

    }
}
